/**
 * 手写 new 操作符实现
 * new 操作符的主要功能：
 * 1. 创建一个新对象
 * 2. 将新对象的原型指向构造函数的 prototype
 * 3. 执行构造函数，并将 this 绑定到新对象
 * 4. 如果构造函数返回对象，则返回该对象，否则返回新创建的对象
 */

function myNew(constructor, ...args) {
    // 1. 参数验证
    if (typeof constructor !== 'function') {
        throw new TypeError('constructor must be a function');
    }
    
    // 2. 创建一个新对象，并将其原型指向构造函数的 prototype
    const obj = Object.create(constructor.prototype);
    
    // 3. 执行构造函数，并将 this 绑定到新创建的对象
    const result = constructor.apply(obj, args);
    
    // 4. 判断构造函数返回值类型
    // 如果构造函数返回一个对象，则返回该对象
    // 否则返回新创建的对象
    const isObject = typeof result === 'object' && result !== null;
    const isFunction = typeof result === 'function';
    
    return isObject || isFunction ? result : obj;
}

// 测试用例
console.log('=== 测试用例 1：普通构造函数 ===');
function Person(name, age) {
    this.name = name;
    this.age = age;
    this.sayHello = function() {
        console.log(`Hello, I'm ${this.name}, ${this.age} years old.`);
    };
}

// 使用原生 new
const person1 = new Person('Alice', 25);
console.log('原生 new 结果:', person1);
person1.sayHello();

// 使用手写 new
const person2 = myNew(Person, 'Bob', 30);
console.log('手写 new 结果:', person2);
person2.sayHello();

console.log('\n=== 测试用例 2：构造函数返回对象 ===');
function Car(brand) {
    this.brand = brand;
    // 返回一个对象，new 操作符会返回这个对象而不是新创建的实例
    return { customBrand: 'Custom ' + brand };
}

const car1 = new Car('BMW');
const car2 = myNew(Car, 'Audi');
console.log('原生 new 返回对象:', car1);
console.log('手写 new 返回对象:', car2);

console.log('\n=== 测试用例 3：构造函数返回基本类型 ===');
function Animal(type) {
    this.type = type;
    // 返回基本类型，new 操作符会忽略并返回新创建的实例
    return 'This will be ignored';
}

const animal1 = new Animal('Dog');
const animal2 = myNew(Animal, 'Cat');
console.log('原生 new 返回基本类型:', animal1);
console.log('手写 new 返回基本类型:', animal2);

console.log('\n=== 测试用例 4：原型链验证 ===');
function Student(name) {
    this.name = name;
}

Student.prototype.study = function() {
    console.log(`${this.name} is studying.`);
};

const student1 = new Student('Tom');
const student2 = myNew(Student, 'Jerry');

console.log('原型链验证 - student1:', student1 instanceof Student);
console.log('原型链验证 - student2:', student2 instanceof Student);
student1.study();
student2.study();

console.log('\n=== 测试用例 5：错误处理 ===');
try {
    const error = myNew('not a function');
} catch (error) {
    console.log('错误处理测试:', error.message);
}

// 性能测试
console.log('\n=== 性能测试 ===');
function TestClass(value) {
    this.value = value;
}

const iterations = 10000;

console.time('原生 new');
for (let i = 0; i < iterations; i++) {
    new TestClass(i);
}
console.timeEnd('原生 new');

console.time('手写 new');
for (let i = 0; i < iterations; i++) {
    myNew(TestClass, i);
}
console.timeEnd('手写 new');

/**
 * new 操作符的详细步骤解析：
 * 
 * 1. 创建一个空的简单 JavaScript 对象（即 {}）
 * 2. 为步骤1新创建的对象添加属性 __proto__，将该属性链接至构造函数的原型对象
 * 3. 将步骤1新创建的对象作为 this 的上下文
 * 4. 如果该函数没有返回对象，则返回 this
 * 
 * 特殊情况的处理：
 * - 如果构造函数返回对象类型（包括函数），则返回该对象
 * - 如果构造函数返回基本类型，则忽略返回值，返回新创建的对象
 * - 如果构造函数抛出异常，则直接抛出异常
 */

module.exports = myNew;